package modelovectorial.persistencia;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import modelovectorial.DocumentoPDF;
import modelovectorial.Palabra;
import modelovectorial.PalabraSucia;
import soporte.MySQLConnexion;
import utn.frc.tsb.utils.ValerioFritelli.SimpleList.SimpleList;

/**
 * @project DLC Final / UTN FRC 2013
 * @date Febrero de 2013
 *
 * @author Gonzalo Arribere
 * @author Gustavo Morales
 * @author Pablo Moreno
 *
 * Esta clase representa a un gestor de inserciones en la base de datos el cual
 * se encarga de realizar todos los insert de la base. Esta clase es
 * implementada a traves del patron Singleton.
 */
public class GestorInsercion {
    // instancia de la clase a devolver

    private static GestorInsercion mINSTANCE = new GestorInsercion();
    // PreparedStatements que contienen las inserciones y actualizaciones precompiladas a ejecutar
    private PreparedStatement updateDocumento, insDocumento, insPalabra, insIncNrDelTermino, insPosteo, insIndice, insTerminoSucio = null;

    public static GestorInsercion getInstance() {
        return mINSTANCE;
    }

    /**
     * Constructor por defecto de la clase.
     */
    private GestorInsercion() {
        this(MySQLConnexion.getConnexion());
    }

    /**
     * Inserta los indices de las palabras con signos dentro de la base de
     * datos.
     */
    public void executeIndicesBatch() {
        try {
            insIndice.executeBatch();
        } catch (SQLException e) {
            Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    /**
     * Inserta los posteo de los terminos con sus documentos en la bd.
     */
    public void executePosteoBatch() {
        try {
            insPosteo.executeBatch();
        } catch (SQLException ex) {
            Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Constructor por parametros de la clase que inicializa los atributos de la
     * clase.
     *
     * @param conn Conexion a la base de datos.
     */
    private GestorInsercion(Connection conn) {
        try {
            insDocumento = conn.prepareStatement("INSERT INTO documento(nombreDocumento, tamañoEnBytes, ruta, cantidadDePaginas, autor, maxTF) VALUES (?,?,?,?,?,?)");

            insPalabra = conn.prepareStatement("INSERT INTO termino(terminoLimpio) VALUES (?)");

            insTerminoSucio = conn.prepareStatement("INSERT INTO terminosucio(idTermino, termino)"
                    + "\nVALUES(?,?)");

            insIncNrDelTermino = conn.prepareStatement("UPDATE termino SET nr = nr + 1 WHERE idTermino = ?");

            insPosteo = conn.prepareStatement("INSERT INTO posteo(idDocumento, idTermino, tf)"
                    + "\nVALUES(?,?,?);");

            insIndice = conn.prepareStatement("INSERT INTO indices(idDocumento, idTerminoSucio, indice)"
                    + "\nVALUES(?,?,?);");

            updateDocumento = conn.prepareStatement("UPDATE documento SET maxTF = ?, cantidadDePaginas = ? WHERE idDocumento = ?");

        } catch (SQLException ex) {
            Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Persiste un documento en la base de datos.
     *
     * @param doc Documento a persistir.
     * @return un entero que si es mayor a cero indica que la insercion se logro
     * exitosamente e igual a cero en caso contrario.
     */
    public int grabarDocumento(DocumentoPDF doc) {
        int resultado_consulta = 0;

        try {
            insDocumento.setString(1, doc.getNombre());
            insDocumento.setLong(2, doc.getLongitud());
            insDocumento.setString(3, doc.getRuta().trim());
            insDocumento.setInt(4, doc.getPaginas());
            insDocumento.setString(5, doc.getAutor());
            insDocumento.setInt(6, doc.getMaxTf());

            resultado_consulta = insDocumento.executeUpdate();
        } catch (SQLException e) {
            Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
        }
        return resultado_consulta;
    }

    /**
     * Persiste un termino limpio en la base de datos junto con sus terminos
     * sucios y tambien realiza el posteo del termino en base a un documento.
     *
     * @param pDocumento Documento a tener en cuenta para el posteo del termino.
     * @param p Palabra a guardar.
     * @return entero que indica si la palabra se grabo exitosamente (cuando es
     * mayor a cero) y no en caso contrario.
     */
    public int grabarPalabra(DocumentoPDF pDocumento, Palabra p) {
        int resultado_consulta = 0;
        // veo si la palabra ya existe, si no existe sigo adelante
        PalabraSucia palabraSucia;

        GestorConsultas consultas = GestorConsultas.getInstance();

        if (!consultas.existePalabra(p)) {
            try {
                insPalabra.setString(1, p.getPalabraLimpia());
                resultado_consulta = insPalabra.executeUpdate();

                SimpleList indices = p.getPs();
                p.setIdPalabra(consultas.getMaximoIdTermino());

                indices.startIterator();
                while (indices.hasNext()) {
                    palabraSucia = (PalabraSucia) indices.next();

                    if (!consultas.existePalabraSucia(palabraSucia)) {
                        insTerminoSucio.setInt(1, p.getIdPalabra());
                        insTerminoSucio.setString(2, palabraSucia.getPalabraSucia());

                        resultado_consulta = insTerminoSucio.executeUpdate();

                        // get en palabraSucia su ID de termino sucio
                        consultas.existePalabraSucia(palabraSucia);
                    }

                    grabarIndice(pDocumento.getIdDocument(),
                            palabraSucia.getIdTerminoSucio(),
                            palabraSucia.getIndices());
                }
            } catch (SQLException e) {
                Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
            }
        } else {
            p.setIdPalabra(p.getIdPalabra());
            this.incrementarNrDeTermino(p);

            SimpleList indices = p.getPs();

            indices.startIterator();
            try {
                while (indices.hasNext()) {
                    palabraSucia = (PalabraSucia) indices.next();

                    if (consultas.existePalabraSucia(palabraSucia) == false) {
                        insTerminoSucio.setInt(1, p.getIdPalabra());
                        insTerminoSucio.setString(2, palabraSucia.getPalabraSucia());
                        resultado_consulta = insTerminoSucio.executeUpdate();
                        consultas.existePalabraSucia(palabraSucia);
                    }
                    grabarIndice(pDocumento.getIdDocument(),
                            palabraSucia.getIdTerminoSucio(),
                            palabraSucia.getIndices());
                }
            } catch (SQLException e) {
                Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, e);
            }
        }

        grabarPosteo(pDocumento.getIdDocument(), p.getIdPalabra(), p.getTfDoc());

        return resultado_consulta;
    }

    /**
     * Incrementa la cantidad de documentos en los que aparecio la palabra
     * recibida por parametro
     *
     * @param p Palabra a incrementar Nr
     * @return un entero que indica si la actualizacion se logro concretar
     * exitosamente (si este es mayor a cero).
     */
    private int incrementarNrDeTermino(Palabra p) {
        int resultado_consulta = 0;

        try {
            insIncNrDelTermino.setInt(1, p.getIdPalabra());
            insIncNrDelTermino.executeUpdate();
        } catch (SQLException e) {
            Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
        }

        return resultado_consulta;
    }

    /**
     * Persiste el posteo de una palabra con su respectivo documento en la bd.
     *
     * @param idDoc identificador del documento.
     * @param pIdTermino identificador del termino
     * @param pFrecuenciaTermino frecuencia del termino en el documento.
     * @return un entero que indica si la insercion fue o no exitosa (sera
     * exitosa si el resultado es mayor a cero)
     */
    public int grabarPosteo(int idDoc, int pIdTermino, int pFrecuenciaTermino) {
        int resultado_consulta = 0;

        try {
            insPosteo.setInt(1, idDoc);
            insPosteo.setInt(2, pIdTermino);
            insPosteo.setInt(3, pFrecuenciaTermino);
            insPosteo.addBatch();
        } catch (SQLException e) {
            Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
        }
        return resultado_consulta;
    }

    /**
     * Persiste los indices de aparicion de un termino con signos dentro de un
     * documento en la base de datos.
     *
     * @param idDoc identificador del documento.
     * @param pIdTerminoSucio identificador del termino con signos.
     * @param pIndices lista simple que contiene los indices del termino con
     * signos.
     * @return un entero que indica si la insercion fue o no exitosa (mayor a
     * cero en caso positivo).
     */
    public int grabarIndice(int idDoc, int pIdTerminoSucio, SimpleList pIndices) {
        int resultado_consulta = 0;
        pIndices.startIterator();
        int indice;
        // recorre palabras sucias y cada palabra tiene una lista de indices
        try {
            while (pIndices.hasNext()) {
                indice = ((Integer) pIndices.next()).intValue();

                insIndice.setInt(1, idDoc);
                insIndice.setInt(2, pIdTerminoSucio);
                insIndice.setInt(3, indice);
                insIndice.addBatch();
            }

        } catch (SQLException e) {
            Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
        }
        return resultado_consulta;
    }

    /**
     * Intenta actualizar los datos de un documento existente en la bd.
     *
     * @param doc objeto documento con los datos para actualizar en la bd.
     * @return un entero que indica si la insercion fue o no exitosa (mayor a
     * cero en caso positivo).
     */
    public int actualizarDocumento(DocumentoPDF doc) {

        int resultado_consulta = 0;
        try {
            updateDocumento.setInt(1, doc.getMaxTf());
            updateDocumento.setInt(2, doc.getPaginas());
            updateDocumento.setInt(3, doc.getIdDocument());

            resultado_consulta = updateDocumento.executeUpdate();
        } catch (SQLException e) {
            Logger.getLogger(GestorConsultas.class.getName()).log(Level.SEVERE, null, e);
        }
        return resultado_consulta;
    }

    /**
     * Deshace los cambios que se realizaron en la bd desde la ultima
     * transaccion ejecutada a traves de un rollback.
     */
    public void deshacerCambios() {
        try {
            MySQLConnexion.getConnexion().rollback();
        } catch (SQLException e) {
            Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    /**
     * Confirma los cambios que se realizaron en la bd desde la ultima
     * transaccion ejecutada a traves de un commit.
     */
    public void confirmarCambios() {
        try {
            MySQLConnexion.getConnexion().commit();
        } catch (SQLException e) {
            Logger.getLogger(GestorInsercion.class.getName()).log(Level.SEVERE, null, e);
        }
    }
}